﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;

using Fuse8.DomainFramework.Common.Exceptions;

namespace Fuse8.DomainFramework.Common.Locator
{
    /// <summary>
    /// 
    /// </summary>
    public class RegisterDependencyContainer
    {
        #region Fields

        private SortedDictionary<string, object> _registerMapper = new SortedDictionary<string, object>();
        private SortedDictionary<string, DependencyDynamicParameter> _registerDynamicMapper = 
            new SortedDictionary<string, DependencyDynamicParameter>();

        private static object _syncRoot = new object();
        private static RegisterDependencyContainer _instance;

        #endregion

        #region Properties

        /// <summary>
        /// 
        /// </summary>
        public static RegisterDependencyContainer Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncRoot)
                    {
                        if (_instance == null)
                        {
                            _instance = new RegisterDependencyContainer();
                        }
                    }
                }

                return _instance;
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// 
        /// </summary>
        protected RegisterDependencyContainer()
        { }

        #endregion

        #region Public methods

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        public void RegisterInstance<T>(T obj)
        {
            Guard.ArgumentNotNull(obj, "obj");

            string typeKey = GenerateTypeKey<T>();
            
            lock (_syncRoot)
            {
                if (_registerMapper.ContainsKey(typeKey))
                {
                    _registerMapper[typeKey] = obj;
                }
                else
                {
                    _registerMapper.Add(typeKey, obj);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public bool IsRegistered<T>()
        {
            string typeKey = GenerateTypeKey<T>();

            return _registerMapper.ContainsKey(typeKey);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="service"></param>
        public void SetDefaultService<T>(T service)
        {
            RegisterInstance(service);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetDefaultService<T>()
        {
            return Resolve<T>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="typeFrom"></param>
        /// <param name="serviceType"></param>
        public void Register(Type typeFrom, Type serviceType)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        /// <param name="injectionMembers"></param>
        public void RegisterType<TFrom, TTo>(params object[] injectionMembers)
        {
            string typeKey = GenerateTypeKey<TFrom>();

            // ensure thread safety
            lock (_syncRoot)
            {
                if (_registerDynamicMapper.ContainsKey(typeKey))
                {
                    _registerDynamicMapper[typeKey] = new DependencyDynamicParameter
                    {
                        DynamicInstanceType = typeof(TTo),
                        InitParameters = injectionMembers
                    };
                }
                else
                {
                    _registerDynamicMapper.Add(typeKey, new DependencyDynamicParameter
                    {
                        DynamicInstanceType = typeof(TTo),
                        InitParameters = injectionMembers
                    });
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="objectType"></param>
        /// <returns></returns>
        public bool IsRegistered(Type objectType)
        {
            string typeKey = GenerateTypeKey(objectType);

            return _registerMapper.ContainsKey(typeKey);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="objectType"></param>
        public void SetDefaultService(Type objectType)
        {
            Guard.ArgumentNotNull(objectType, "objectType");

            string typeKey = GenerateTypeKey(objectType);

            lock (_syncRoot)
            {
                if (_registerDynamicMapper.ContainsKey(typeKey))
                {
                    _registerMapper.Add(typeKey, new DependencyDynamicParameter
                    {
                        DynamicInstanceType = objectType
                    });
                }
                else
                {
                    _registerMapper[typeKey] = new DependencyDynamicParameter
                    {
                        DynamicInstanceType = objectType
                    };
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="objectType"></param>
        /// <returns></returns>
        public object GetDefaultService(Type objectType)
        {
            return Resolve(objectType);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Resolve<T>()
        {            
            T result = default(T);

            string typeKey = GenerateTypeKey<T>();

            // ensure thread safety
            lock (_syncRoot)
            {
                result = _registerMapper.ContainsKey(typeKey) ?
                    (T)_registerMapper[typeKey] :
                    default(T);

                if (result == null)
                {
                    var dependencyDynamicParameter = _registerDynamicMapper.ContainsKey(typeKey) ?
                        _registerDynamicMapper[typeKey] :
                        null;

                    if (dependencyDynamicParameter != null)
                    {
                        result = (T)Activator.CreateInstance(dependencyDynamicParameter.DynamicInstanceType, 
                            dependencyDynamicParameter.InitParameters);
                    }
                }

                if (result == null)
                {
                    throw new RegisterDependencyException(string.Format(CultureInfo.CurrentUICulture, Resource.UnresolvedDependency, typeKey));
                }
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="objectType"></param>
        /// <returns></returns>
        public object Resolve(Type objectType)
        {
            Guard.ArgumentNotNull(objectType, "objectType");

            object result = null;

            string typeKey = GenerateTypeKey(objectType);

            lock (_syncRoot)
            {
                result = _registerMapper.ContainsKey(typeKey) ?
                                   _registerMapper[typeKey] :
                                   null;

                if (result == null)
                {
                    var dependencyDynamicParameter = _registerDynamicMapper.ContainsKey(typeKey) ?
                        _registerDynamicMapper[typeKey] :
                        null;

                    if (dependencyDynamicParameter != null)
                    {
                        result = Activator.CreateInstance(dependencyDynamicParameter.DynamicInstanceType,
                            dependencyDynamicParameter.InitParameters);
                    }
                }

                if (result == null)
                {
                    throw new RegisterDependencyException(string.Format(CultureInfo.CurrentUICulture, Resource.UnresolvedDependency, typeKey));
                }
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="objectType"></param>
        /// <returns></returns>
        public IEnumerable<object> ResolveAll(Type objectType)
        {
            Guard.ArgumentNotNull(objectType, "objectType");

            return new object[] { Resolve(objectType) };
        }

        #endregion

        #region Private methods

        private static string GenerateTypeKey<T>()
        {
            return typeof(T).AssemblyQualifiedName;
        }

        private static string GenerateTypeKey(Type objectType)
        {
            Guard.ArgumentNotNull(objectType, "objectType");
            
            return objectType.AssemblyQualifiedName;
        }

        #endregion
    }
}
